We will begin by running a simple linear model that regresses weekly
sales onto Consumer Price Index (CPI)
# Specifying our model type and setting the computational engine
linear_model <-
linear_reg() %>%
set_engine("lm")
# Fitting the model
fit_cpi <-
linear_model %>%
fit(weekly_sales ~ cpi, data = dfw)
# Model output
summary(fit_cpi$fit)
Call:
stats::lm(formula = weekly_sales ~ cpi, data = data)
Residuals:
Min 1Q Median 3Q Max
-662386 -318443 -73868 258442 2095880
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 827280.5 21778.4 37.986 < 2e-16 ***
cpi -732.7 123.7 -5.923 3.33e-09 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 390600 on 6433 degrees of freedom
Multiple R-squared: 0.005423, Adjusted R-squared: 0.005269
F-statistic: 35.08 on 1 and 6433 DF, p-value: 3.332e-09
In this model, a Walmart store with a theoretical square footage of
0 can expect its weekly sales to be ~$828,280 if CPI is
held constant. We also observe that the relationship between
Weekly_Sales and CPI is negative. That is, if CPI increases by one unit,
weekly sales will decrease by ~$733; and if CPI decreases by one unit,
sales would increase by ~$733.
In evaluating the model statistics, we can see an Adjusted R_Squared
value of 0.005269. In other words, this model explains only roughly 0.5%
of the variance in Walmart’s weekly sales. So, while our interpretation
of the effect of CPI on Weekly_Sales is still valid, we must
conclude that this model appears to fail in explaining the variance in
our target variable.
# We'plot the affect of CPI on sales for a few different stores in the dataset, starting with store 10.
plot_store_10 <-
dfw %>%
filter(Store == 10) %>%
ggplot(aes(x = CPI, y = Weekly_Sales)) +
geom_point() +
geom_smooth(method = "lm") +
labs(title = 'Weekly Sales vs. CPI for Store 10', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal()
filter: removed 6,292 rows (98%), 143 rows remaining
plotly::ggplotly(plot_store_10)
`geom_smooth()` using formula = 'y ~ x'
plot_store_11 <-
dfw %>%
filter(Store == 11) %>%
ggplot(aes(x = CPI, y = Weekly_Sales)) +
geom_point() +
geom_smooth(method = "lm") +
labs(title = 'Weekly Sales vs. CPI for Store 11', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal()
filter: removed 6,292 rows (98%), 143 rows remaining
plotly::ggplotly(plot_store_11)
`geom_smooth()` using formula = 'y ~ x'
plot_store_12 <-
dfw %>%
filter(Store == 10) %>%
ggplot(aes(x = CPI, y = Weekly_Sales)) +
geom_point() +
geom_smooth(method = "lm") +
labs(title = 'Weekly Sales vs. CPI for Store 12', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal()
filter: removed 6,292 rows (98%), 143 rows remaining
plotly::ggplotly(plot_store_12)
`geom_smooth()` using formula = 'y ~ x'
plot_store_13 <-
dfw %>%
filter(Store == 13) %>%
ggplot(aes(x = CPI, y = Weekly_Sales)) +
geom_point() +
geom_smooth(method = "lm") +
labs(title = 'Weekly Sales vs. CPI for Store 13', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal()
filter: removed 6,292 rows (98%), 143 rows remaining
plotly::ggplotly(plot_store_13)
`geom_smooth()` using formula = 'y ~ x'
# A plot to demonstrate the fluctuation of CPI by region/store. Note that the
# smoothed line is negative in some locales and positive in others.
animated_plot <-
dfw %>%
filter(store %in% c(11:15)) %>%
ggplot(aes(x = cpi, y = weekly_sales)) +
geom_point() +
geom_smooth(method = lm) +
labs(title = 'Weekly Sales vs. CPI for Store {closest_state}', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal() +
gganimate::transition_states(store, transition_length = 1, state_length = 2) +
gganimate::view_follow()
animated_plot
Inserting image 1 at 0.00s (1%)...
Inserting image 2 at 0.10s (2%)...
Inserting image 3 at 0.20s (3%)...
Inserting image 4 at 0.30s (4%)...
Inserting image 5 at 0.40s (5%)...
Inserting image 6 at 0.50s (6%)...
Inserting image 7 at 0.60s (7%)...
Inserting image 8 at 0.70s (8%)...
Inserting image 9 at 0.80s (9%)...
Inserting image 10 at 0.90s (10%)...
Inserting image 11 at 1.00s (11%)...
Inserting image 12 at 1.10s (12%)...
Inserting image 13 at 1.20s (13%)...
Inserting image 14 at 1.30s (14%)...
Inserting image 15 at 1.40s (15%)...
Inserting image 16 at 1.50s (16%)...
Inserting image 17 at 1.60s (17%)...
Inserting image 18 at 1.70s (18%)...
Inserting image 19 at 1.80s (19%)...
Inserting image 20 at 1.90s (20%)...
Inserting image 21 at 2.00s (21%)...
Inserting image 22 at 2.10s (22%)...
Inserting image 23 at 2.20s (23%)...
Inserting image 24 at 2.30s (24%)...
Inserting image 25 at 2.40s (25%)...
Inserting image 26 at 2.50s (26%)...
Inserting image 27 at 2.60s (27%)...
Inserting image 28 at 2.70s (28%)...
Inserting image 29 at 2.80s (29%)...
Inserting image 30 at 2.90s (30%)...
Inserting image 31 at 3.00s (31%)...
Inserting image 32 at 3.10s (32%)...
Inserting image 33 at 3.20s (33%)...
Inserting image 34 at 3.30s (34%)...
Inserting image 35 at 3.40s (35%)...
Inserting image 36 at 3.50s (36%)...
Inserting image 37 at 3.60s (37%)...
Inserting image 38 at 3.70s (38%)...
Inserting image 39 at 3.80s (39%)...
Inserting image 40 at 3.90s (40%)...
Inserting image 41 at 4.00s (41%)...
Inserting image 42 at 4.10s (42%)...
Inserting image 43 at 4.20s (43%)...
Inserting image 44 at 4.30s (44%)...
Inserting image 45 at 4.40s (45%)...
Inserting image 46 at 4.50s (46%)...
Inserting image 47 at 4.60s (47%)...
Inserting image 48 at 4.70s (48%)...
Inserting image 49 at 4.80s (49%)...
Inserting image 50 at 4.90s (50%)...
Inserting image 51 at 5.00s (51%)...
Inserting image 52 at 5.10s (52%)...
Inserting image 53 at 5.20s (53%)...
Inserting image 54 at 5.30s (54%)...
Inserting image 55 at 5.40s (55%)...
Inserting image 56 at 5.50s (56%)...
Inserting image 57 at 5.60s (57%)...
Inserting image 58 at 5.70s (58%)...
Inserting image 59 at 5.80s (59%)...
Inserting image 60 at 5.90s (60%)...
Inserting image 61 at 6.00s (61%)...
Inserting image 62 at 6.10s (62%)...
Inserting image 63 at 6.20s (63%)...
Inserting image 64 at 6.30s (64%)...
Inserting image 65 at 6.40s (65%)...
Inserting image 66 at 6.50s (66%)...
Inserting image 67 at 6.60s (67%)...
Inserting image 68 at 6.70s (68%)...
Inserting image 69 at 6.80s (69%)...
Inserting image 70 at 6.90s (70%)...
Inserting image 71 at 7.00s (71%)...
Inserting image 72 at 7.10s (72%)...
Inserting image 73 at 7.20s (73%)...
Inserting image 74 at 7.30s (74%)...
Inserting image 75 at 7.40s (75%)...
Inserting image 76 at 7.50s (76%)...
Inserting image 77 at 7.60s (77%)...
Inserting image 78 at 7.70s (78%)...
Inserting image 79 at 7.80s (79%)...
Inserting image 80 at 7.90s (80%)...
Inserting image 81 at 8.00s (81%)...
Inserting image 82 at 8.10s (82%)...
Inserting image 83 at 8.20s (83%)...
Inserting image 84 at 8.30s (84%)...
Inserting image 85 at 8.40s (85%)...
Inserting image 86 at 8.50s (86%)...
Inserting image 87 at 8.60s (87%)...
Inserting image 88 at 8.70s (88%)...
Inserting image 89 at 8.80s (89%)...
Inserting image 90 at 8.90s (90%)...
Inserting image 91 at 9.00s (91%)...
Inserting image 92 at 9.10s (92%)...
Inserting image 93 at 9.20s (93%)...
Inserting image 94 at 9.30s (94%)...
Inserting image 95 at 9.40s (95%)...
Inserting image 96 at 9.50s (96%)...
Inserting image 97 at 9.60s (97%)...
Inserting image 98 at 9.70s (98%)...
Inserting image 99 at 9.80s (99%)...
Inserting image 100 at 9.90s (100%)...
Encoding to gif... done!
What we observe here is that the impact of CPI can vary greatly by
store/region. This still aligns with our evaluation of fit_cpi because
we recall that that particular model explained only a small amount (~5%)
of the variance in Weekly_Sales, so we would expect to see these kinds
of swings. With a (much) higher Adjusted R-Squared, these variations
would look unusual.
dfw %>%
group_by(store) %>%
group_modify(~ tidy(lm(weekly_sales ~ ., data = .x))) %>%
filter(term == "cpi")
NA
# Filtering for 2012 and plotting CPI against Weekly_Sales
plot <- dfw %>%
filter(lubridate::year(date) == 2012) %>%
ggplot(aes(x=cpi, y = weekly_sales)) +
geom_point() +
geom_smooth(method=lm)
labs(title = 'Weekly Sales vs. CPI for Store 12', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal()
NULL
plotly::ggplotly(plot)
NA
We see an interesting effect when we filter for one specific year.
The clusters are nearly vertical because CPI is calculated
geographically, with either Core Based Statistical Area (CBSA) or
Metropolitan Statistical Area (MSA). CPI might be the same in a
particular region, but different stores in that region will have
different sales volume, hence the vertical clusters.
plot_store_cpi <- dfw %>%
filter(store==10, lubridate::year(date)==2012) %>%
ggplot(aes(x=cpi, y = weekly_sales)) +
geom_point() +
geom_smooth(method=lm)
Quitting from lines 189-198 (lab-II-template.Rmd)
labs(title = 'Weekly Sales vs. CPI for Store 10 in 2012', x = 'Consumer Price Index', y = 'Weekly Sales (USD)') +
theme_minimal()
NULL
plotly::ggplotly(plot_store_cpi)
Although CPI varies by region, the deviation in CPI across time for
a single region tends to be much lower, which is why we see such a slim
range here. Since CPI is a measure of inflation, we expect to see these
regional effects.
# A new iteration of the previous model that also includes store Size as an
# independent variable
options(scipen = 999)
fit_cpi_size <-
linear_model %>%
fit(weekly_sales ~ cpi + size, data = dfw)
summary(fit_cpi_size$fit)
Call:
stats::lm(formula = weekly_sales ~ cpi + size, data = data)
Residuals:
Min 1Q Median 3Q Max
-563750 -167145 -29612 112172 1912650
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 182831.50332 14966.91316 12.216 <0.0000000000000002
cpi -657.04633 76.92121 -8.542 <0.0000000000000002
size 4.84669 0.04796 101.048 <0.0000000000000002
(Intercept) ***
cpi ***
size ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 242800 on 6432 degrees of freedom
Multiple R-squared: 0.6156, Adjusted R-squared: 0.6155
F-statistic: 5151 on 2 and 6432 DF, p-value: < 0.00000000000000022
# Comparing fit_cpi to fit_size to see which is better at explaining the variance
# in Weekly_Sales
anova(fit_cpi$fit, fit_cpi_size$fit)
Analysis of Variance Table
Model 1: Weekly_Sales ~ CPI
Model 2: Weekly_Sales ~ CPI + Size
Res.Df RSS Df Sum of Sq F Pr(>F)
1 6433 9.8128e+14
2 6432 3.7924e+14 1 6.0204e+14 10211 < 2.2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Note also that the coefficient in the revised model has been reduced
from ~$733 to ~$657. This is simply due to the fact that size is now
explaining more of the variance that was left unexplained by the
previous model that only included CPI.
# Building a model that uses all variables EXCEPT Date and Store
fit_full <-
linear_model %>%
fit(weekly_sales ~ . - store - date, data = dfw)
summary(fit_full$fit)
Call:
stats::lm(formula = weekly_sales ~ . - store - date, data = data)
Residuals:
Min 1Q Median 3Q Max
-557148 -165608 -24125 112851 1918479
Coefficients:
Estimate Std. Error t value
(Intercept) 313268.64179 35462.52571 8.834
isholidayTRUE 60120.82723 11961.41705 5.026
temperature 1001.63556 173.85005 5.761
fuel_price -13332.38798 6821.86906 -1.954
cpi -946.07156 84.45115 -11.203
unemployment -12517.11110 1724.67790 -7.258
size 4.83971 0.04802 100.786
Pr(>|t|)
(Intercept) < 0.0000000000000002 ***
isholidayTRUE 0.00000051374714 ***
temperature 0.00000000872334 ***
fuel_price 0.0507 .
cpi < 0.0000000000000002 ***
unemployment 0.00000000000044 ***
size < 0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 241200 on 6428 degrees of freedom
Multiple R-squared: 0.621, Adjusted R-squared: 0.6206
F-statistic: 1755 on 6 and 6428 DF, p-value: < 0.00000000000000022
anova(fit_cpi_size$fit, fit_full$fit)
Analysis of Variance Table
Model 1: Weekly_Sales ~ CPI + Size
Model 2: Weekly_Sales ~ (Store + Date + IsHoliday + Temperature + Fuel_Price +
CPI + Unemployment + Size) - Store - Date
Res.Df RSS Df Sum of Sq F Pr(>F)
1 6432 3.7924e+14
2 6428 3.7394e+14 4 5.3028e+12 22.789 < 2.2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
We observe a further, though slight improvement in the Adjusted
R-Squared value in the new model that eliminates temporal and regional
effects (fit_full). The ANOVA test also confirms that the improvement in
explanatory power is indeed statistically significant.
More Linear Regression
We hypothesize that the effect of good weather is increased on
holidays. We can test this by revising fit_full and including an
interaction term.
fit_full_int <-
linear_model %>%
fit(weekly_sales ~ . - store - date + isholiday * temperature, data = dfw)
summary(fit_full_int$fit)
Call:
stats::lm(formula = weekly_sales ~ . - store - date + isholiday *
temperature, data = data)
Residuals:
Min 1Q Median 3Q Max
-557499 -165415 -24493 112914 1918376
Coefficients:
Estimate Std. Error t value
(Intercept) 314781.71540 35650.02554 8.830
isholidayTRUE 47453.37499 32654.55081 1.453
temperature 980.88764 180.84372 5.424
fuel_price -13421.90341 6825.68548 -1.966
cpi -945.95168 84.45707 -11.200
unemployment -12511.43706 1724.84245 -7.254
size 4.83969 0.04802 100.779
isholidayTRUE:temperature 247.28642 593.15059 0.417
Pr(>|t|)
(Intercept) < 0.0000000000000002 ***
isholidayTRUE 0.1462
temperature 0.000000060421363 ***
fuel_price 0.0493 *
cpi < 0.0000000000000002 ***
unemployment 0.000000000000453 ***
size < 0.0000000000000002 ***
isholidayTRUE:temperature 0.6768
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 241200 on 6427 degrees of freedom
Multiple R-squared: 0.621, Adjusted R-squared: 0.6206
F-statistic: 1504 on 7 and 6427 DF, p-value: < 0.00000000000000022
anova(fit_full$fit, fit_full_int$fit)
Analysis of Variance Table
Model 1: weekly_sales ~ (store + date + isholiday + temperature + fuel_price +
cpi + unemployment + size) - store - date
Model 2: weekly_sales ~ (store + date + isholiday + temperature + fuel_price +
cpi + unemployment + size) - store - date + isholiday * temperature
Res.Df RSS Df Sum of Sq F Pr(>F)
1 6428 373938635272534
2 6427 373928522950003 1 10112322531 0.1738 0.6768
Although the results of our fit_full_int model demonstrate that the
effect of good weather is indeed more significant on holidays, the ANOVA
test shows no statistically significant improvement. We cannot assert
definitively that this model with the interaction term is an
improvement.
We’ll also test whether the effect of temperature on weekly sales is
linear by squaring that variable.
fit_full_sq <-
linear_model %>%
fit(weekly_sales ~ . - store - date + I(temperature ^2), data = dfw)
summary(fit_full_sq$fit)
Call:
stats::lm(formula = weekly_sales ~ . - store - date + I(temperature^2),
data = data)
Residuals:
Min 1Q Median 3Q Max
-561455 -165260 -24674 112058 1911166
Coefficients:
Estimate Std. Error t value
(Intercept) 261043.07789 41108.23517 6.350
isholidayTRUE 62296.69695 11987.90765 5.197
temperature 3293.89228 930.05089 3.542
fuel_price -14713.82683 6841.25650 -2.151
cpi -954.71915 84.48673 -11.300
unemployment -12529.36093 1723.97501 -7.268
size 4.83146 0.04811 100.420
I(temperature^2) -19.82165 7.90072 -2.509
Pr(>|t|)
(Intercept) 0.000000000229814 ***
isholidayTRUE 0.000000209190349 ***
temperature 0.0004 ***
fuel_price 0.0315 *
cpi < 0.0000000000000002 ***
unemployment 0.000000000000409 ***
size < 0.0000000000000002 ***
I(temperature^2) 0.0121 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 241100 on 6427 degrees of freedom
Multiple R-squared: 0.6214, Adjusted R-squared: 0.621
F-statistic: 1507 on 7 and 6427 DF, p-value: < 0.00000000000000022
anova(fit_full$fit, fit_full_sq$fit)
Analysis of Variance Table
Model 1: weekly_sales ~ (store + date + isholiday + temperature + fuel_price +
cpi + unemployment + size) - store - date
Model 2: weekly_sales ~ (store + date + isholiday + temperature + fuel_price +
cpi + unemployment + size) - store - date + I(temperature^2)
Res.Df RSS Df Sum of Sq F Pr(>F)
1 6428 373938635272534
2 6427 373572776702821 1 365858569713 6.2943 0.01214 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
## Plotting the relationship between Temperature^2 and Weekly Sales
dfw %>%
ggplot(aes(x = temperature, y = weekly_sales)) +
geom_smooth(method = "lm", formula = y ~ x + I(x^2))

The model output demonstrates a curvilinear, or inverted U-shaped
relationship (visualized below). People are less likely to shop retail
on a freezing cold day. Increasing temperatures are associated with
increased sales, but only to a point. As temperatures become excessive
and dangerous, sales start to decrease.
If we were managing Walmart’s promotions we could offer larger
discounts when the whether is at either extreme and perhaps even
increase the price of certain products when the temperature is
mild.
Predictive Analytics
Now that we have a model that is fairly robust we will use it to
make predictions of weekly sales revenue.
# Setting seed for reproducibility
set.seed(3.14159)
# Splitting the data set into a training dataset (75%) and a test dataset (25%)
dfw_split <- initial_split(dfw)
dfw_train <- training(dfw_split)
dfw_test <- testing(dfw_split)
# Fitting the model
fit_org <-
linear_model %>%
fit(weekly_sales ~ . - date - store + I(temperature^2), data = dfw_train)
summary(fit_org$fit)
Call:
stats::lm(formula = weekly_sales ~ . - date - store + I(temperature^2),
data = data)
Residuals:
Min 1Q Median 3Q Max
-557260 -165114 -25112 115048 1913671
Coefficients:
Estimate Std. Error t value
(Intercept) 254646.56714 47252.27399 5.389
isholidayTRUE 60380.17646 13967.26022 4.323
temperature 3056.33287 1068.34983 2.861
fuel_price -19393.30976 7819.05402 -2.480
cpi -921.70456 96.40208 -9.561
unemployment -10579.94848 1991.83421 -5.312
size 4.82603 0.05496 87.809
I(temperature^2) -16.27913 9.05811 -1.797
Pr(>|t|)
(Intercept) 0.0000000742 ***
isholidayTRUE 0.0000157042 ***
temperature 0.00424 **
fuel_price 0.01316 *
cpi < 0.0000000000000002 ***
unemployment 0.0000001135 ***
size < 0.0000000000000002 ***
I(temperature^2) 0.07237 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 239000 on 4818 degrees of freedom
Multiple R-squared: 0.6248, Adjusted R-squared: 0.6242
F-statistic: 1146 on 7 and 4818 DF, p-value: < 0.00000000000000022
# The linear regression output as a tibble
tidy(fit_org)
# Creating a new dataframe with predicted values
results_org <-
predict(fit_org, new_data = dfw_test) %>%
bind_cols(dfw_test) %>%
rename(Predicted_Sales = .pred)
results_org %>%
arrange(date)
NA
# Defining the metric set we will be working with to evaluate the models
perf_metrics <- metric_set(rmse, mae)
# Calculating the performance of fit
perf_metrics(results_org, truth = weekly_sales, estimate = Predicted_Sales)
When we remove the temperature term, something notable occurs.
First, our Adjusted R-Squared value diminishes slighlty (from 62.2% to
62.1%), making it a slightly less appealing model in terms of explaining
the variance in weekly sales. However, we also observe that the error
has been reduced, making fit_nosq relatively superior in terms of
predictive capability. Since we are trying to build a reliable
predictive model, we exclude the term and conclude that
fit_nosq is better for that purpose.
More Predictive Modeling
We are fairly pleased with both the explanatory and predictive power
of fit_nosq but of course we would like to improve upon both metrics.
One issue that we have not yet discussed is the variability in weekly
sales across Walmart locations, as shown below.
# Calculating total weekly sales per store
sales_by_store <- aggregate(weekly_sales ~ store, data = dfw, sum)
# A bar chart showing the distribution of weekly sales revenue by store
ggplot(sales_by_store, aes(x = store, y = weekly_sales)) +
geom_bar(stat = "identity", fill = "#0078D4") +
labs(title = "Total Weekly Sales by Store", x = "Store Number", y = "Total Weekly Sales")

This final model uses a log-linear regression to explain weekly
Walmart sales using store-level, economic, and seasonal predictors.
Applying a log transformation to weekly sales substantially improves
model performance, yielding an adjusted R² of 0.71, and stabilizes
variance across stores with vastly different revenue scales. Overall
model fit is strong, with well-behaved residuals and a highly
significant F-statistic, indicating that the included predictors jointly
explain a meaningful share of sales variation.
Results show that store size is the dominant driver of weekly sales,
dwarfing macroeconomic effects and confirming that physical scale
largely determines revenue potential. Holiday weeks are associated with
an average 6–7% increase in sales, validating the importance of seasonal
demand spikes. Inflation, proxied by CPI, has a small but statistically
significant negative relationship with sales, even after controlling for
store characteristics. Temperature and unemployment exhibit modest
effects consistent with economic intuition, while fuel prices do not
appear to meaningfully impact sales once other factors are accounted
for.
This specification represents the best balance between
interpretability, explanatory power, and robustness among the models
tested. The log transformation enables clear percentage-based
interpretations while materially improving fit relative to linear
alternatives, making the model suitable for both analytical insight and
downstream forecasting.
Limitiations
- The model does not explicitly account for store-level fixed
effects or regional hierarchies, which may mask persistent
location-specific dynamics.
- Temporal structure is handled implicitly; autocorrelation and
seasonality are not directly modeled.
- The analysis assumes linear relationships on the log scale and may
understate nonlinear or interaction effects beyond those tested.
- CPI and unemployment are measured at broader geographic levels and
may not fully capture local economic conditions.
Next Steps
- Implement mixed-effects (hierarchical) models to capture
store-specific variation.
- Explore time-series approaches for improved short-term
forecasting.
LS0tCnRpdGxlOiAiV2FsbWFydCdzIFdlZWtseSBTYWxlcyBMaW5lYXIgUmVncmVzc2lvbiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6CiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgKICBodG1sX25vdGVib29rOiBkZWZhdWx0CmFsd2F5c19hbGxvd19odG1sOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKCmBgYAoKKioqCgpgYGB7cn0KbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgidGlkeW1vZGVscyIpCmxpYnJhcnkoInRpZHlsb2ciKQoKYGBgCgoKYGBge3J9CgojIFJlYWRpbmcgaW4gdGhlIFdhbG1hcnQgZGF0YXNldDoKCmRmdyA8LSByZWFkX2NzdigiZGF0YS93YWxtYXJ0LmNzdiIpICU+JSAKICByZW5hbWVfd2l0aCh0b2xvd2VyKSAlPiUgCiAgYXJyYW5nZShzdG9yZSkKCgpoZWFkKGRmdykKCmBgYApgYGB7cn0KI0FuIG92ZXJ2aWV3IG9mIG91ciBkYXRhc2V0J3Mgc3RydWN0dXJlCgpzdHIoZGZ3KQpgYGAKCmBgYHtyfQojQW4gb3ZlcnZpZXcgb2YgdGhlIGRhdGEKCmhlYWQoZGZ3KQpgYGAKCmBgYHtyfQoKI1RoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgYW5kIGVhY2ggY29sdW1uJ3MgZGF0YSB0eXBlLiAgTm90ZSB0aGF0IGlzaG9saWRheSBpcyBhIGxvZ2ljYWwgKGkuZS4sIHRydWUvZmFsc2UpIHByZWRpY3RvciB2YXJpYWJsZQoKc3RyKGRmdykKCmBgYAoKCjxoMSBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij5TaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWw8L2gxPgoKIyMjIyBXZSB3aWxsIGJlZ2luIGJ5IHJ1bm5pbmcgYSBzaW1wbGUgbGluZWFyIG1vZGVsIHRoYXQgcmVncmVzc2VzIHdlZWtseSBzYWxlcyBvbnRvIENvbnN1bWVyIFByaWNlIEluZGV4IChDUEkpCgoKYGBge3J9CiMgU3BlY2lmeWluZyBvdXIgbW9kZWwgdHlwZSBhbmQgc2V0dGluZyB0aGUgY29tcHV0YXRpb25hbCBlbmdpbmUKCmxpbmVhcl9tb2RlbCA8LSAKICBsaW5lYXJfcmVnKCkgJT4lIAogIHNldF9lbmdpbmUoImxtIikKCiMgRml0dGluZyB0aGUgbW9kZWwKCmZpdF9jcGkgPC0gCiAgbGluZWFyX21vZGVsICU+JSAKICBmaXQod2Vla2x5X3NhbGVzIH4gY3BpLCBkYXRhID0gZGZ3KQogIAojIE1vZGVsIG91dHB1dCAKCnN1bW1hcnkoZml0X2NwaSRmaXQpCgpgYGAKCgojIyMjIyBJbiB0aGlzIG1vZGVsLCBhIFdhbG1hcnQgc3RvcmUgd2l0aCBhIHRoZW9yZXRpY2FsIHNxdWFyZSBmb290YWdlIG9mIDAgY2FuIGV4cGVjdCBpdHMgd2Vla2x5IHNhbGVzIHRvIGJlICoqfiQ4MjgsMjgwKiogaWYgQ1BJIGlzIGhlbGQgY29uc3RhbnQuICBXZSBhbHNvIG9ic2VydmUgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gV2Vla2x5X1NhbGVzIGFuZCBDUEkgaXMgbmVnYXRpdmUuICBUaGF0IGlzLCBpZiBDUEkgaW5jcmVhc2VzIGJ5IG9uZSB1bml0LCB3ZWVrbHkgc2FsZXMgd2lsbCBkZWNyZWFzZSBieSB+JDczMzsgYW5kIGlmIENQSSBkZWNyZWFzZXMgYnkgb25lIHVuaXQsIHNhbGVzIHdvdWxkIGluY3JlYXNlIGJ5IH4kNzMzLgoKCiMjIyMjIEluIGV2YWx1YXRpbmcgdGhlIG1vZGVsIHN0YXRpc3RpY3MsIHdlIGNhbiBzZWUgYW4gQWRqdXN0ZWQgUl9TcXVhcmVkIHZhbHVlIG9mIDAuMDA1MjY5LiBJbiBvdGhlciB3b3JkcywgdGhpcyBtb2RlbCBleHBsYWlucyBvbmx5IHJvdWdobHkgMC41JSBvZiB0aGUgdmFyaWFuY2UgaW4gV2FsbWFydCdzIHdlZWtseSBzYWxlcy4gIFNvLCB3aGlsZSBvdXIgaW50ZXJwcmV0YXRpb24gb2YgdGhlICplZmZlY3QqIG9mIENQSSBvbiBXZWVrbHlfU2FsZXMgaXMgc3RpbGwgdmFsaWQsIHdlIG11c3QgY29uY2x1ZGUgdGhhdCB0aGlzIG1vZGVsIGFwcGVhcnMgdG8gZmFpbCBpbiBleHBsYWluaW5nIHRoZSB2YXJpYW5jZSBpbiBvdXIgdGFyZ2V0IHZhcmlhYmxlLgoKCmBgYHtyfQojIE5vdyB3ZSB3aWxsIHBsb3QgdGhlIGFmZmVjdCBvZiBDUEkgb24gc2FsZXMgZm9yIGEgZmV3IGRpZmZlcmVudCBzdG9yZXMgaW4gdGhlIGRhdGFzZXQsIHN0YXJ0aW5nIHdpdGggc3RvcmUgMTAuCgpwbG90X3N0b3JlXzEwIDwtIAogIGRmdyAlPiUgCiAgZmlsdGVyKHN0b3JlID09IDEwKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gY3BpLCB5ID0gd2Vla2x5X3NhbGVzKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICAgIGxhYnModGl0bGUgPSAnV2Vla2x5IFNhbGVzIHZzLiBDUEkgZm9yIFN0b3JlIDEwJywgeCA9ICdDb25zdW1lciBQcmljZSBJbmRleCcsIHkgPSAnV2Vla2x5ICAgU2FsZXMgKFVTRCknKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwbG90bHk6OmdncGxvdGx5KHBsb3Rfc3RvcmVfMTApCgoKcGxvdF9zdG9yZV8xMSA8LSAKICBkZncgJT4lIAogIGZpbHRlcihzdG9yZSA9PSAxMSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGNwaSwgeSA9IHdlZWtseV9zYWxlcykpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgICBsYWJzKHRpdGxlID0gJ1dlZWtseSBTYWxlcyB2cy4gQ1BJIGZvciBTdG9yZSAxMScsIHggPSAnQ29uc3VtZXIgUHJpY2UgSW5kZXgnLCB5ID0gJ1dlZWtseSAgIFNhbGVzIChVU0QpJykgKwogIHRoZW1lX21pbmltYWwoKQoKcGxvdGx5OjpnZ3Bsb3RseShwbG90X3N0b3JlXzExKQoKcGxvdF9zdG9yZV8xMiA8LSAKICBkZncgJT4lIAogIGZpbHRlcihzdG9yZSA9PSAxMCkgJT4lIAogIGdncGxvdChhZXMoeCA9IGNwaSwgeSA9IHdlZWtseV9zYWxlcykpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgICBsYWJzKHRpdGxlID0gJ1dlZWtseSBTYWxlcyB2cy4gQ1BJIGZvciBTdG9yZSAxMicsIHggPSAnQ29uc3VtZXIgUHJpY2UgSW5kZXgnLCB5ID0gJ1dlZWtseSAgIFNhbGVzIChVU0QpJykgKwogIHRoZW1lX21pbmltYWwoKQoKcGxvdGx5OjpnZ3Bsb3RseShwbG90X3N0b3JlXzEyKQoKcGxvdF9zdG9yZV8xMyA8LSAKICBkZncgJT4lIAogIGZpbHRlcihzdG9yZSA9PSAxMykgJT4lIAogIGdncGxvdChhZXMoeCA9IGNwaSwgeSA9IHdlZWtseV9zYWxlcykpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgICBsYWJzKHRpdGxlID0gJ1dlZWtseSBTYWxlcyB2cy4gQ1BJIGZvciBTdG9yZSAxMycsIHggPSAnQ29uc3VtZXIgUHJpY2UgSW5kZXgnLCB5ID0gJ1dlZWtseSAgIFNhbGVzIChVU0QpJykgKwogIHRoZW1lX21pbmltYWwoKQoKcGxvdGx5OjpnZ3Bsb3RseShwbG90X3N0b3JlXzEzKQoKYGBgCgpgYGB7cn0KCiMgQSBwbG90IHRvIGRlbW9uc3RyYXRlIHRoZSBmbHVjdHVhdGlvbiBvZiBDUEkgYnkgcmVnaW9uL3N0b3JlLiAgTm90ZSB0aGF0IHRoZQojIHNtb290aGVkIGxpbmUgaXMgbmVnYXRpdmUgaW4gc29tZSBsb2NhbGVzIGFuZCBwb3NpdGl2ZSBpbiBvdGhlcnMuCgphbmltYXRlZF9wbG90IDwtIAogICAgZGZ3ICU+JSAKICAgIGZpbHRlcihzdG9yZSAlaW4lIGMoMTE6MTUpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBjcGksIHkgPSB3ZWVrbHlfc2FsZXMpKSArIAogICAgZ2VvbV9wb2ludCgpICsgCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKwogICAgbGFicyh0aXRsZSA9ICdXZWVrbHkgU2FsZXMgdnMuIENQSSBmb3IgU3RvcmUge2Nsb3Nlc3Rfc3RhdGV9JywgeCA9ICdDb25zdW1lciBQcmljZSBJbmRleCcsIHkgPSAnV2Vla2x5IFNhbGVzIChVU0QpJykgKwogICAgdGhlbWVfbWluaW1hbCgpICsgCiAgICBnZ2FuaW1hdGU6OnRyYW5zaXRpb25fc3RhdGVzKHN0b3JlLCB0cmFuc2l0aW9uX2xlbmd0aCA9IDEsIHN0YXRlX2xlbmd0aCA9IDIpICsKICAgIGdnYW5pbWF0ZTo6dmlld19mb2xsb3coKQoKYW5pbWF0ZWRfcGxvdApgYGAKCgojIyMjIyBXaGF0IHdlIG9ic2VydmUgaGVyZSBpcyB0aGF0IHRoZSBpbXBhY3Qgb2YgQ1BJIGNhbiB2YXJ5IGdyZWF0bHkgYnkgc3RvcmUvcmVnaW9uLiAgVGhpcyBzdGlsbCBhbGlnbnMgd2l0aCBvdXIgZXZhbHVhdGlvbiBvZiBmaXRfY3BpIGJlY2F1c2Ugd2UgcmVjYWxsIHRoYXQgdGhhdCBwYXJ0aWN1bGFyIG1vZGVsIGV4cGxhaW5lZCBvbmx5IGEgc21hbGwgYW1vdW50ICh+NSUpIG9mIHRoZSB2YXJpYW5jZSBpbiBXZWVrbHlfU2FsZXMsIHNvIHdlIHdvdWxkIGV4cGVjdCB0byBzZWUgdGhlc2Uga2luZHMgb2Ygc3dpbmdzLiAgV2l0aCBhIChtdWNoKSBoaWdoZXIgQWRqdXN0ZWQgUi1TcXVhcmVkLCB0aGVzZSB2YXJpYXRpb25zIHdvdWxkIGxvb2sgdW51c3VhbC4KCgpgYGB7cn0KCmRmdyAlPiUKICBncm91cF9ieShzdG9yZSkgJT4lCiAgZ3JvdXBfbW9kaWZ5KH4gdGlkeShsbSh3ZWVrbHlfc2FsZXMgfiAuLCBkYXRhID0gLngpKSkgJT4lCiAgZmlsdGVyKHRlcm0gPT0gImNwaSIpCgpgYGAKCgpgYGB7cn0KIyBGaWx0ZXJpbmcgZm9yIDIwMTIgYW5kIHBsb3R0aW5nIENQSSBhZ2FpbnN0IFdlZWtseV9TYWxlcwoKcGxvdCA8LSBkZncgJT4lCgkgICAgICAJZmlsdGVyKGx1YnJpZGF0ZTo6eWVhcihkYXRlKSA9PSAyMDEyKSAlPiUKCSAgICAgICAgICBnZ3Bsb3QoYWVzKHg9Y3BpLCB5ID0gd2Vla2x5X3NhbGVzKSkgKwoJICAgICAgICAgIGdlb21fcG9pbnQoKSArCiAJICAgICAgICAJZ2VvbV9zbW9vdGgobWV0aG9kPWxtKQogICAgbGFicyh0aXRsZSA9ICdXZWVrbHkgU2FsZXMgdnMuIENQSSBmb3IgU3RvcmUgMTInLCB4ID0gJ0NvbnN1bWVyIFByaWNlIEluZGV4JywgeSA9ICdXZWVrbHkgICBTYWxlcyAoVVNEKScpICsKICB0aGVtZV9taW5pbWFsKCkKICAgIApwbG90bHk6OmdncGxvdGx5KHBsb3QpCgpgYGAKCgojIyMjIyBXZSBzZWUgYW4gaW50ZXJlc3RpbmcgZWZmZWN0IHdoZW4gd2UgZmlsdGVyIGZvciBvbmUgc3BlY2lmaWMgeWVhci4gVGhlIGNsdXN0ZXJzIGFyZSBuZWFybHkgdmVydGljYWwgYmVjYXVzZSBDUEkgaXMgY2FsY3VsYXRlZCBnZW9ncmFwaGljYWxseSwgd2l0aCBlaXRoZXIgQ29yZSBCYXNlZCBTdGF0aXN0aWNhbCBBcmVhIChDQlNBKSBvciBNZXRyb3BvbGl0YW4gU3RhdGlzdGljYWwgQXJlYSAoTVNBKS4gIENQSSBtaWdodCBiZSB0aGUgc2FtZSBpbiBhIHBhcnRpY3VsYXIgcmVnaW9uLCBidXQgZGlmZmVyZW50IHN0b3JlcyBpbiB0aGF0IHJlZ2lvbiB3aWxsIGhhdmUgZGlmZmVyZW50IHNhbGVzIHZvbHVtZSwgaGVuY2UgdGhlIHZlcnRpY2FsIGNsdXN0ZXJzLgoKCmBgYHtyfQojIE5vdyBsZXQncyBsb29rIGV4Y2x1c2l2ZWx5IGF0IHN0b3JlIDEwCgpwbG90X3N0b3JlX2NwaSA8LSBkZncgJT4lCiAgICAgIAkJZmlsdGVyKHN0b3JlPT0xMCwgbHVicmlkYXRlOjp5ZWFyKGRhdGUpPT0yMDEyKSAlPiUKICAgICAgCSAgICBnZ3Bsb3QoYWVzKHg9Y3BpLCB5ID0gd2Vla2x5X3NhbGVzKSkgKwogICAgICAJICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD1sbSkKICAgIGxhYnModGl0bGUgPSAnV2Vla2x5IFNhbGVzIHZzLiBDUEkgZm9yIFN0b3JlIDEwIGluIDIwMTInLCB4ID0gJ0NvbnN1bWVyIFByaWNlIEluZGV4JywgeSA9ICdXZWVrbHkgICBTYWxlcyAoVVNEKScpICsKICB0aGVtZV9taW5pbWFsKCkKCnBsb3RseTo6Z2dwbG90bHkocGxvdF9zdG9yZV9jcGkpCmBgYAoKCiMjIyMjIEFsdGhvdWdoIENQSSB2YXJpZXMgYnkgcmVnaW9uLCB0aGUgZGV2aWF0aW9uIGluIENQSSBhY3Jvc3MgdGltZSBmb3IgYSBzaW5nbGUgcmVnaW9uIHRlbmRzIHRvIGJlIG11Y2ggbG93ZXIsIHdoaWNoIGlzIHdoeSB3ZSBzZWUgc3VjaCBhIHNsaW0gcmFuZ2UgaGVyZS4gU2luY2UgQ1BJIGlzIGEgbWVhc3VyZSBvZiBpbmZsYXRpb24sIHdlIGV4cGVjdCB0byBzZWUgdGhlc2UgcmVnaW9uYWwgZWZmZWN0cy4KCgpgYGB7cn0KCiMgQSBuZXcgaXRlcmF0aW9uIG9mIHRoZSBwcmV2aW91cyBtb2RlbCB0aGF0IGFsc28gaW5jbHVkZXMgc3RvcmUgU2l6ZSBhcyBhbiBpbmRlcGVuZGVudCB2YXJpYWJsZQoKZml0X2NwaV9zaXplIDwtIAogIGxpbmVhcl9tb2RlbCAlPiUgCiAgZml0KHdlZWtseV9zYWxlcyB+IGNwaSArIHNpemUsIGRhdGEgPSBkZncpCgpzdW1tYXJ5KGZpdF9jcGlfc2l6ZSRmaXQpCgpgYGAKCmBgYHtyfQojIENvbXBhcmluZyBmaXRfY3BpIHRvIGZpdF9zaXplIHRvIHNlZSB3aGljaCBpcyBiZXR0ZXIgYXQgZXhwbGFpbmluZyB0aGUgdmFyaWFuY2UgaW4gV2Vla2x5X1NhbGVzCgphbm92YShmaXRfY3BpJGZpdCwgZml0X2NwaV9zaXplJGZpdCkKYGBgCgoKIyMjIyMgVGhlIG1vZGVsIHRoYXQgaW5jbHVkZXMgc2l6ZSBhcyBhIHByZWRpY3RvciB2YXJpYWJsZSAoZml0X2NwaV9zaXplKSBhcHBlYXJzIHRvIHBlcmZvcm0gc2lnbmlmaWNhbnRseSBiZXR0ZXIgdGhhbiBmaXRfY3BpLiAgQWRqdXN0ZWQgUi1TcXVhcmUgbm93IGV4cGxhaW5zIH42MiUgb2YgdGhlIHZhcmlhbmNlIGluIHJlbnRhbHMgYW5kIHRoZSBBTk9WQSB0ZXN0IGNvbmZpcm1zIHRoYXQgaW5jbHVkaW5nIHNpemUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4KCgpgYGB7cn0KdGlkeShmaXRfY3BpJGZpdCkKdGlkeShmaXRfY3BpX3NpemUkZml0KQpgYGAKCgojIyMjIyBOb3RlIGFsc28gdGhhdCB0aGUgY29lZmZpY2llbnQgaW4gdGhlIHJldmlzZWQgbW9kZWwgaGFzIGJlZW4gcmVkdWNlZCBmcm9tIH4kNzMzIHRvIH4kNjU3LiAgVGhpcyBpcyBzaW1wbHkgZHVlIHRvIHRoZSBmYWN0IHRoYXQgc2l6ZSBpcyBub3cgZXhwbGFpbmluZyBtb3JlIG9mIHRoZSB2YXJpYW5jZSB0aGF0IHdhcyBsZWZ0IHVuZXhwbGFpbmVkIGJ5IHRoZSBwcmV2aW91cyBtb2RlbCB0aGF0IG9ubHkgaW5jbHVkZWQgQ1BJLgoKCmBgYHtyfQojIEJ1aWxkaW5nIGEgbW9kZWwgdGhhdCB1c2VzIGFsbCB2YXJpYWJsZXMgRVhDRVBUIERhdGUgYW5kIFN0b3JlCgpmaXRfZnVsbCA8LSAKICBsaW5lYXJfbW9kZWwgJT4lIAogIGZpdCh3ZWVrbHlfc2FsZXMgfiAuIC0gc3RvcmUgLSBkYXRlLCBkYXRhID0gZGZ3KQoKc3VtbWFyeShmaXRfZnVsbCRmaXQpCgpgYGAKYGBge3J9CmFub3ZhKGZpdF9jcGlfc2l6ZSRmaXQsIGZpdF9mdWxsJGZpdCkKYGBgCgoKIyMjIyMgV2Ugb2JzZXJ2ZSBhIGZ1cnRoZXIsIHRob3VnaCBzbGlnaHQgaW1wcm92ZW1lbnQgaW4gdGhlIEFkanVzdGVkIFItU3F1YXJlZCB2YWx1ZSBpbiB0aGUgbmV3IG1vZGVsIHRoYXQgZWxpbWluYXRlcyB0ZW1wb3JhbCBhbmQgcmVnaW9uYWwgZWZmZWN0cyAoZml0X2Z1bGwpLiAgVGhlIEFOT1ZBIHRlc3QgYWxzbyBjb25maXJtcyB0aGF0IHRoZSBpbXByb3ZlbWVudCBpbiBleHBsYW5hdG9yeSBwb3dlciBpcyBpbmRlZWQgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4KCjxoMSBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij5Nb3JlIExpbmVhciBSZWdyZXNzaW9uPC9oMT4KCiMjIyMjIFdlIGh5cG90aGVzaXplIHRoYXQgdGhlIGVmZmVjdCBvZiBnb29kIHdlYXRoZXIgaXMgaW5jcmVhc2VkIG9uIGhvbGlkYXlzLiAgV2UgY2FuIHRlc3QgdGhpcyBieSByZXZpc2luZyBmaXRfZnVsbCBhbmQgaW5jbHVkaW5nIGFuIGludGVyYWN0aW9uIHRlcm0uCgoKYGBge3J9CmZpdF9mdWxsX2ludCA8LSAKICBsaW5lYXJfbW9kZWwgJT4lIAogIGZpdCh3ZWVrbHlfc2FsZXMgfiAuIC0gc3RvcmUgLSBkYXRlICsgaXNob2xpZGF5ICogdGVtcGVyYXR1cmUsIGRhdGEgPSBkZncpCgpzdW1tYXJ5KGZpdF9mdWxsX2ludCRmaXQpCmBgYApgYGB7cn0KYW5vdmEoZml0X2Z1bGwkZml0LCBmaXRfZnVsbF9pbnQkZml0KQpgYGAKCgojIyMjIyBBbHRob3VnaCB0aGUgcmVzdWx0cyBvZiBvdXIgZml0X2Z1bGxfaW50IG1vZGVsIGRlbW9uc3RyYXRlIHRoYXQgdGhlIGVmZmVjdCBvZiBnb29kIHdlYXRoZXIgaXMgaW5kZWVkIG1vcmUgc2lnbmlmaWNhbnQgb24gaG9saWRheXMsIHRoZSBBTk9WQSB0ZXN0IHNob3dzIG5vIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnQuICBXZSBjYW5ub3QgYXNzZXJ0IGRlZmluaXRpdmVseSB0aGF0IHRoaXMgbW9kZWwgd2l0aCB0aGUgaW50ZXJhY3Rpb24gdGVybSBpcyBhbiBpbXByb3ZlbWVudC4KCiMjIyMjIFdlJ2xsIGFsc28gdGVzdCB3aGV0aGVyIHRoZSBlZmZlY3Qgb2YgdGVtcGVyYXR1cmUgb24gd2Vla2x5IHNhbGVzIGlzIGxpbmVhciBieSBzcXVhcmluZyB0aGF0IHZhcmlhYmxlLgoKCmBgYHtyfQpmaXRfZnVsbF9zcSA8LSAKICBsaW5lYXJfbW9kZWwgJT4lIAogIGZpdCh3ZWVrbHlfc2FsZXMgfiAuIC0gc3RvcmUgLSBkYXRlICsgSSh0ZW1wZXJhdHVyZSBeMiksIGRhdGEgPSBkZncpCgpzdW1tYXJ5KGZpdF9mdWxsX3NxJGZpdCkKYGBgCmBgYHtyfQphbm92YShmaXRfZnVsbCRmaXQsIGZpdF9mdWxsX3NxJGZpdCkKYGBgCgpgYGB7cn0KIyMgUGxvdHRpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFRlbXBlcmF0dXJlXjIgYW5kIFdlZWtseSBTYWxlcyAKCmRmdyAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0ZW1wZXJhdHVyZSwgeSA9IHdlZWtseV9zYWxlcykpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiB4ICsgSSh4XjIpKQoKYGBgCgoKIyMjIyMgVGhlIG1vZGVsIG91dHB1dCBkZW1vbnN0cmF0ZXMgYSBjdXJ2aWxpbmVhciwgb3IgaW52ZXJ0ZWQgVS1zaGFwZWQgcmVsYXRpb25zaGlwICh2aXN1YWxpemVkIGJlbG93KS4gIFBlb3BsZSBhcmUgbGVzcyBsaWtlbHkgdG8gc2hvcCByZXRhaWwgb24gYSBmcmVlemluZyBjb2xkIGRheS4gSW5jcmVhc2luZyB0ZW1wZXJhdHVyZXMgYXJlIGFzc29jaWF0ZWQgd2l0aCBpbmNyZWFzZWQgc2FsZXMsIGJ1dCBvbmx5IHRvIGEgcG9pbnQuICBBcyB0ZW1wZXJhdHVyZXMgYmVjb21lIGV4Y2Vzc2l2ZSBhbmQgZGFuZ2Vyb3VzLCBzYWxlcyBzdGFydCB0byBkZWNyZWFzZS4KCiMjIyMjIElmIHdlIHdlcmUgbWFuYWdpbmcgV2FsbWFydCdzIHByb21vdGlvbnMgd2UgY291bGQgb2ZmZXIgbGFyZ2VyIGRpc2NvdW50cyB3aGVuIHRoZSB3aGV0aGVyIGlzIGF0IGVpdGhlciBleHRyZW1lIGFuZCBwZXJoYXBzIGV2ZW4gaW5jcmVhc2UgdGhlIHByaWNlIG9mIGNlcnRhaW4gcHJvZHVjdHMgd2hlbiB0aGUgdGVtcGVyYXR1cmUgaXMgbWlsZC4KCjxoMSBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij5QcmVkaWN0aXZlIEFuYWx5dGljczwvaDE+CgojIyMjIyBOb3cgdGhhdCB3ZSBoYXZlIGEgbW9kZWwgdGhhdCBpcyBmYWlybHkgcm9idXN0IHdlIHdpbGwgdXNlIGl0IHRvIG1ha2UgcHJlZGljdGlvbnMgb2Ygd2Vla2x5IHNhbGVzIHJldmVudWUuCgoKYGBge3J9CiMgU2V0dGluZyBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKCnNldC5zZWVkKDMuMTQxNTkpCgojIFNwbGl0dGluZyB0aGUgZGF0YSBzZXQgaW50byBhIHRyYWluaW5nIGRhdGFzZXQgKDc1JSkgYW5kIGEgdGVzdCBkYXRhc2V0ICgyNSUpCgpkZndfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChkZncpCgpkZndfdHJhaW4gPC0gIHRyYWluaW5nKGRmd19zcGxpdCkKCmRmd190ZXN0IDwtIHRlc3RpbmcoZGZ3X3NwbGl0KQpgYGAKCmBgYHtyfQojIEZpdHRpbmcgdGhlIG1vZGVsCgpmaXRfb3JnIDwtIAogIGxpbmVhcl9tb2RlbCAlPiUgCiAgZml0KHdlZWtseV9zYWxlcyB+IC4gLSBkYXRlIC0gc3RvcmUgKyBJKHRlbXBlcmF0dXJlXjIpLCBkYXRhID0gZGZ3X3RyYWluKQoKc3VtbWFyeShmaXRfb3JnJGZpdCkKICAKYGBgCgpgYGB7cn0KIyBUaGUgbGluZWFyIHJlZ3Jlc3Npb24gb3V0cHV0IGFzIGEgdGliYmxlCgp0aWR5KGZpdF9vcmcpCmBgYApgYGB7cn0KCiMgQ3JlYXRpbmcgYSBuZXcgZGF0YWZyYW1lIHdpdGggcHJlZGljdGVkIHZhbHVlcwoKcmVzdWx0c19vcmcgPC0KICBwcmVkaWN0KGZpdF9vcmcsIG5ld19kYXRhID0gZGZ3X3Rlc3QpICU+JSAKICBiaW5kX2NvbHMoZGZ3X3Rlc3QpICU+JSAKICByZW5hbWUoUHJlZGljdGVkX1NhbGVzID0gLnByZWQpCgpyZXN1bHRzX29yZyAlPiUgCiAgYXJyYW5nZShkYXRlKQoKYGBgCgpgYGB7cn0KIyBEZWZpbmluZyB0aGUgbWV0cmljIHNldCB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCB0byBldmFsdWF0ZSB0aGUgbW9kZWxzCgpwZXJmX21ldHJpY3MgPC0gbWV0cmljX3NldChybXNlLCBtYWUpCmBgYAoKCmBgYHtyfQoKIyBDYWxjdWxhdGluZyB0aGUgcGVyZm9ybWFuY2Ugb2YgZml0CgpwZXJmX21ldHJpY3MocmVzdWx0c19vcmcsIHRydXRoID0gIHdlZWtseV9zYWxlcywgZXN0aW1hdGUgPSBQcmVkaWN0ZWRfU2FsZXMpCmBgYAoKCiMjIyMjIFRoZXNlIG1ldHJpY3MgaW5kaWNhdGUgdGhhdCBvdXIgbW9kZWwgaXMgb2ZmIGJ5IH4kMjQwLDQyNCBhY2NvcmRpbmcgdG8gUk1TRSBhbmQgfiQxNzksMDkyIE1BRS4gIFRoZXNlIG51bWJlcnMgYXBwZWFyIGFsYXJtaW5nIHVudGlsIG9uZSByZWNhbGxzIHRoYXQgdGhlIHJhbmdlIG9mIHdlZWtseSBzYWxlcyBieSBXYWxtYXJ0IHN0b3JlIGxvY2F0aW9uIGlzIGFib3V0ICQ3MGsgdG8gJDIuOCBtaWxsaW9uLCB3aXRoIGEgbWVhbiBvZiAkNzQwayBhbmQgYSBtZWRpYW4gb2YgJDY4OWsuCgoKYGBge3J9CiNCdWlsZGluZyB0aGUgbW9kZWwgd2l0aG91dCBJKFRlbXBlcmF0dXJlXjIpIHZhcmlhYmxlIHVzaW5nIG9ubHkgdGhlIHRyYWluaW5nIGRhdGEgc2V0CgpmaXRfb3JnX25vc3EgPC0KICBsaW5lYXJfbW9kZWwgJT4lIAogIGZpdCh3ZWVrbHlfc2FsZXMgfiAuIC0gc3RvcmUgLSBkYXRlLCBkYXRhID0gZGZ3X3RyYWluKQoKYGBgCgpgYGB7cn0Kc3VtbWFyeShmaXRfb3JnX25vc3EkZml0KQpgYGAKCgpgYGB7cn0KI0NvbXBhcmluZyB0aGUgbW9kZWxzCgphbm92YShmaXRfb3JnJGZpdCwgZml0X29yZ19ub3NxJGZpdCkKYGBgCgoKYGBge3J9CiNDcmVhdGluZyBhIG5ldyBkYXRhZnJhbWUgcmVzdWx0c19vcmdfbm9zcSB3aXRoIHByZWRpY3Rpb25zCgpyZXN1bHRzX29yZ19ub3NxIDwtCiAgcHJlZGljdChmaXRfb3JnX25vc3EsIG5ld19kYXRhID0gZGZ3X3Rlc3QpICU+JSAKICBiaW5kX2NvbHMoZGZ3X3Rlc3QpICU+JSAKICByZW5hbWUoUHJlZGljdGVkX1NhbGVzID0gLnByZWQpCgojQ2FsY3VsYXRpbmcgcGVyZm9ybWFuY2UgbWV0cmljcwoKcGVyZl9tZXRyaWNzKHJlc3VsdHNfb3JnX25vc3EsIHRydXRoID0gIHdlZWtseV9zYWxlcywgZXN0aW1hdGUgPSBQcmVkaWN0ZWRfU2FsZXMpCmBgYAoKCiMjIyMjIFdoZW4gd2UgcmVtb3ZlIHRoZSB0ZW1wZXJhdHVyZSB0ZXJtLCBzb21ldGhpbmcgbm90YWJsZSBvY2N1cnMuICBGaXJzdCwgb3VyIEFkanVzdGVkIFItU3F1YXJlZCB2YWx1ZSBkaW1pbmlzaGVzIHNsaWdobHR5IChmcm9tIDYyLjIlIHRvIDYyLjElKSwgbWFraW5nIGl0IGEgc2xpZ2h0bHkgbGVzcyBhcHBlYWxpbmcgbW9kZWwgaW4gdGVybXMgb2YgZXhwbGFpbmluZyB0aGUgdmFyaWFuY2UgaW4gd2Vla2x5IHNhbGVzLiAgSG93ZXZlciwgd2UgYWxzbyBvYnNlcnZlIHRoYXQgdGhlIGVycm9yIGhhcyBiZWVuIHJlZHVjZWQsIG1ha2luZyBmaXRfbm9zcSByZWxhdGl2ZWx5IHN1cGVyaW9yIGluIHRlcm1zIG9mIHByZWRpY3RpdmUgY2FwYWJpbGl0eS4gU2luY2Ugd2UgYXJlIHRyeWluZyB0byBidWlsZCBhIHJlbGlhYmxlICpwcmVkaWN0aXZlKiBtb2RlbCwgd2UgZXhjbHVkZSB0aGUgdGVybSBhbmQgY29uY2x1ZGUgdGhhdCBmaXRfbm9zcSBpcyBiZXR0ZXIgZm9yIHRoYXQgcHVycG9zZS4KCjxoMSBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij5Nb3JlIFByZWRpY3RpdmUgTW9kZWxpbmc8L2gxPgoKIyMjIyMgV2UgYXJlIGZhaXJseSBwbGVhc2VkIHdpdGggYm90aCB0aGUgZXhwbGFuYXRvcnkgYW5kIHByZWRpY3RpdmUgcG93ZXIgb2YgZml0X25vc3EgYnV0IG9mIGNvdXJzZSB3ZSB3b3VsZCBsaWtlIHRvIGltcHJvdmUgdXBvbiBib3RoIG1ldHJpY3MuICBPbmUgaXNzdWUgdGhhdCB3ZSBoYXZlIG5vdCB5ZXQgZGlzY3Vzc2VkIGlzIHRoZSB2YXJpYWJpbGl0eSBpbiB3ZWVrbHkgc2FsZXMgYWNyb3NzIFdhbG1hcnQgbG9jYXRpb25zLCBhcyBzaG93biBiZWxvdy4KCgpgYGB7cn0KIyBDYWxjdWxhdGluZyB0b3RhbCB3ZWVrbHkgc2FsZXMgcGVyIHN0b3JlCgpzYWxlc19ieV9zdG9yZSA8LSBhZ2dyZWdhdGUod2Vla2x5X3NhbGVzIH4gc3RvcmUsIGRhdGEgPSBkZncsIHN1bSkKCiMgQSBiYXIgY2hhcnQgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHdlZWtseSBzYWxlcyByZXZlbnVlIGJ5IHN0b3JlCgpnZ3Bsb3Qoc2FsZXNfYnlfc3RvcmUsIGFlcyh4ID0gc3RvcmUsIHkgPSB3ZWVrbHlfc2FsZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiIzAwNzhENCIpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFdlZWtseSBTYWxlcyBieSBTdG9yZSIsIHggPSAiU3RvcmUgTnVtYmVyIiwgeSA9ICJUb3RhbCBXZWVrbHkgU2FsZXMiKQoKYGBgCgoKIyMjIyMgVmlld2luZyB0aGUgYmFyIGNoYXJ0LCB3ZSBjYW4gc2VlIHRoYXQgc3RhbmRhcmRpemluZyB0aGUgd2Vla2x5X3NhbGVzIHZhcmlhYmxlIGNvdWxkIGltcHJvdmUgb3VyIG1vZGVsLiAgT25lIHdheSB3ZSBjb3VsZCBkbyB0aGlzIGlzIGJ5IHRyYW5zZm9ybWluZyB0aGUgc2NhbGUgb2Ygd2Vla2x5IHNhbGVzLCB0cmFuc2Zvcm1pbmcgZWFjaCB2YWx1ZSB0byBpdHMgbmF0dXJhbCBsb2dhcml0aG1pYyB2YWx1ZS4gIFdlJ2xsIHVzZSB0aGUgbG9nKCkgZnVuY3Rpb24gdG8gYWNjb21wbGlzaCB0aGlzLgoKCmBgYHtyfQojIExvZyBtb2RlbCB3aXRoIGFsbCBvdGhlciB2YXJpYWJsZXMgdW5jaGFuZ2VkCmRmd19sb2cgPC0KICBkZncgJT4lIAogIG11dGF0ZShsb2dfc2FsZXMgPSBsb2cod2Vla2x5X3NhbGVzKSkKCmRmd19sb2cKYGBgCmBgYHtyfQpzZXQuc2VlZCgzLjE0MTU5KQoKZGZ3bG9nX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQoZGZ3X2xvZykKZGZ3bG9nX3RyYWluIDwtIHRyYWluaW5nKGRmd2xvZ19zcGxpdCkKZGZ3bG9nX3Rlc3QgPC0gdGVzdGluZyhkZndsb2dfc3BsaXQpCmBgYAoKYGBge3J9CmZpdF9sb2cgPC0gCiAgbGluZWFyX21vZGVsICU+JSAKICBmaXQobG9nX3NhbGVzIH4gLiAtIHN0b3JlIC0gZGF0ZSAtIHdlZWtseV9zYWxlcywgZGF0YT1kZndsb2dfdHJhaW4pCgpzdW1tYXJ5KGZpdF9sb2ckZml0KQpgYGAKIyMjIyMgVGhpcyBmaW5hbCBtb2RlbCB1c2VzIGEgbG9nLWxpbmVhciByZWdyZXNzaW9uIHRvIGV4cGxhaW4gd2Vla2x5IFdhbG1hcnQgc2FsZXMgdXNpbmcgc3RvcmUtbGV2ZWwsIGVjb25vbWljLCBhbmQgc2Vhc29uYWwgcHJlZGljdG9ycy4gQXBwbHlpbmcgYSBsb2cgdHJhbnNmb3JtYXRpb24gdG8gd2Vla2x5IHNhbGVzIHN1YnN0YW50aWFsbHkgaW1wcm92ZXMgbW9kZWwgcGVyZm9ybWFuY2UsIHlpZWxkaW5nIGFuIGFkanVzdGVkIFLCsiBvZiAwLjcxLCBhbmQgc3RhYmlsaXplcyB2YXJpYW5jZSBhY3Jvc3Mgc3RvcmVzIHdpdGggdmFzdGx5IGRpZmZlcmVudCByZXZlbnVlIHNjYWxlcy4gT3ZlcmFsbCBtb2RlbCBmaXQgaXMgc3Ryb25nLCB3aXRoIHdlbGwtYmVoYXZlZCByZXNpZHVhbHMgYW5kIGEgaGlnaGx5IHNpZ25pZmljYW50IEYtc3RhdGlzdGljLCBpbmRpY2F0aW5nIHRoYXQgdGhlIGluY2x1ZGVkIHByZWRpY3RvcnMgam9pbnRseSBleHBsYWluIGEgbWVhbmluZ2Z1bCBzaGFyZSBvZiBzYWxlcyB2YXJpYXRpb24uCmBgYHtyfQpgYGAKIyMjIyMgUmVzdWx0cyBzaG93IHRoYXQgc3RvcmUgc2l6ZSBpcyB0aGUgZG9taW5hbnQgZHJpdmVyIG9mIHdlZWtseSBzYWxlcywgZHdhcmZpbmcgbWFjcm9lY29ub21pYyBlZmZlY3RzIGFuZCBjb25maXJtaW5nIHRoYXQgcGh5c2ljYWwgc2NhbGUgbGFyZ2VseSBkZXRlcm1pbmVzIHJldmVudWUgcG90ZW50aWFsLiBIb2xpZGF5IHdlZWtzIGFyZSBhc3NvY2lhdGVkIHdpdGggYW4gYXZlcmFnZSA24oCTNyUgaW5jcmVhc2UgaW4gc2FsZXMsIHZhbGlkYXRpbmcgdGhlIGltcG9ydGFuY2Ugb2Ygc2Vhc29uYWwgZGVtYW5kIHNwaWtlcy4gSW5mbGF0aW9uLCBwcm94aWVkIGJ5IENQSSwgaGFzIGEgc21hbGwgYnV0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgbmVnYXRpdmUgcmVsYXRpb25zaGlwIHdpdGggc2FsZXMsIGV2ZW4gYWZ0ZXIgY29udHJvbGxpbmcgZm9yIHN0b3JlIGNoYXJhY3RlcmlzdGljcy4gVGVtcGVyYXR1cmUgYW5kIHVuZW1wbG95bWVudCBleGhpYml0IG1vZGVzdCBlZmZlY3RzIGNvbnNpc3RlbnQgd2l0aCBlY29ub21pYyBpbnR1aXRpb24sIHdoaWxlIGZ1ZWwgcHJpY2VzIGRvIG5vdCBhcHBlYXIgdG8gbWVhbmluZ2Z1bGx5IGltcGFjdCBzYWxlcyBvbmNlIG90aGVyIGZhY3RvcnMgYXJlIGFjY291bnRlZCBmb3IuCmBgYHtyfQpgYGAKIyMjIyMgVGhpcyBzcGVjaWZpY2F0aW9uIHJlcHJlc2VudHMgdGhlIGJlc3QgYmFsYW5jZSBiZXR3ZWVuIGludGVycHJldGFiaWxpdHksIGV4cGxhbmF0b3J5IHBvd2VyLCBhbmQgcm9idXN0bmVzcyBhbW9uZyB0aGUgbW9kZWxzIHRlc3RlZC4gVGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiBlbmFibGVzIGNsZWFyIHBlcmNlbnRhZ2UtYmFzZWQgaW50ZXJwcmV0YXRpb25zIHdoaWxlIG1hdGVyaWFsbHkgaW1wcm92aW5nIGZpdCByZWxhdGl2ZSB0byBsaW5lYXIgYWx0ZXJuYXRpdmVzLCBtYWtpbmcgdGhlIG1vZGVsIHN1aXRhYmxlIGZvciBib3RoIGFuYWx5dGljYWwgaW5zaWdodCBhbmQgZG93bnN0cmVhbSBmb3JlY2FzdGluZy4KCjxoMSBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij5MaW1pdGlhdGlvbnM8L2gxPgoKIyMjIyMgLSBUaGUgbW9kZWwgZG9lcyBub3QgZXhwbGljaXRseSBhY2NvdW50IGZvciBzdG9yZS1sZXZlbCBmaXhlZCBlZmZlY3RzIG9yIHJlZ2lvbmFsIGhpZXJhcmNoaWVzLCB3aGljaCBtYXkgbWFzayBwZXJzaXN0ZW50IGxvY2F0aW9uLXNwZWNpZmljIGR5bmFtaWNzLgoKIyMjIyMgLSBUZW1wb3JhbCBzdHJ1Y3R1cmUgaXMgaGFuZGxlZCBpbXBsaWNpdGx5OyBhdXRvY29ycmVsYXRpb24gYW5kIHNlYXNvbmFsaXR5IGFyZSBub3QgZGlyZWN0bHkgbW9kZWxlZC4KCiMjIyMjIC0gVGhlIGFuYWx5c2lzIGFzc3VtZXMgbGluZWFyIHJlbGF0aW9uc2hpcHMgb24gdGhlIGxvZyBzY2FsZSBhbmQgbWF5IHVuZGVyc3RhdGUgbm9ubGluZWFyIG9yIGludGVyYWN0aW9uIGVmZmVjdHMgYmV5b25kIHRob3NlIHRlc3RlZC4KCiMjIyMjIC0gQ1BJIGFuZCB1bmVtcGxveW1lbnQgYXJlIG1lYXN1cmVkIGF0IGJyb2FkZXIgZ2VvZ3JhcGhpYyBsZXZlbHMgYW5kIG1heSBub3QgZnVsbHkgY2FwdHVyZSBsb2NhbCBlY29ub21pYyBjb25kaXRpb25zLgoKPGgxIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjsiPk5leHQgU3RlcHM8L2gxPgoKIyMjIyMgLSBJbXBsZW1lbnQgbWl4ZWQtZWZmZWN0cyAoaGllcmFyY2hpY2FsKSBtb2RlbHMgdG8gY2FwdHVyZSBzdG9yZS1zcGVjaWZpYyB2YXJpYXRpb24uCgojIyMjIyAtIEV4cGxvcmUgdGltZS1zZXJpZXMgYXBwcm9hY2hlcyBmb3IgaW1wcm92ZWQgc2hvcnQtdGVybSBmb3JlY2FzdGluZy4KCiMjIyMjIC0gSW5jb3Jwb3JhdGUgcHJvbW90aW9uYWwgZGF0YSwgZm9vdCB0cmFmZmljLCBvciBsb2NhbCBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMgdG8gZW5oYW5jZSBwcmVkaWN0aXZlIGFjY3VyYWN5Lgo=